import type { Express } from "express";
import { createServer, type Server } from "http";
import { WebSocketServer, WebSocket } from "ws";
import { storage } from "./storage";
import { proxyManager } from "./proxy-manager";
import { insertProxyRuleSchema, insertBackendServerSchema } from "@shared/schema";
import { z } from "zod";

export async function registerRoutes(app: Express): Promise<Server> {
  const httpServer = createServer(app);

  // WebSocket server for real-time updates
  const wss = new WebSocketServer({ server: httpServer, path: '/ws' });
  
  wss.on('connection', (ws: WebSocket) => {
    console.log('WebSocket client connected');
    proxyManager.addWebSocketConnection(ws);
    
    ws.on('close', () => {
      console.log('WebSocket client disconnected');
    });
  });

  // Proxy Rules API
  app.get("/api/proxy-rules", async (req, res) => {
    try {
      const rules = await storage.getProxyRules();
      res.json(rules);
    } catch (error) {
      res.status(500).json({ message: "Failed to fetch proxy rules" });
    }
  });

  app.get("/api/proxy-rules/:id", async (req, res) => {
    try {
      const id = parseInt(req.params.id);
      const rule = await storage.getProxyRule(id);
      if (!rule) {
        return res.status(404).json({ message: "Proxy rule not found" });
      }
      res.json(rule);
    } catch (error) {
      res.status(500).json({ message: "Failed to fetch proxy rule" });
    }
  });

  app.post("/api/proxy-rules", async (req, res) => {
    try {
      const validatedData = insertProxyRuleSchema.parse(req.body);
      const rule = await storage.createProxyRule(validatedData);
      res.status(201).json(rule);
    } catch (error) {
      if (error instanceof z.ZodError) {
        return res.status(400).json({ message: "Invalid input", errors: error.errors });
      }
      res.status(500).json({ message: "Failed to create proxy rule" });
    }
  });

  app.put("/api/proxy-rules/:id", async (req, res) => {
    try {
      const id = parseInt(req.params.id);
      const validatedData = insertProxyRuleSchema.partial().parse(req.body);
      const rule = await storage.updateProxyRule(id, validatedData);
      if (!rule) {
        return res.status(404).json({ message: "Proxy rule not found" });
      }
      res.json(rule);
    } catch (error) {
      if (error instanceof z.ZodError) {
        return res.status(400).json({ message: "Invalid input", errors: error.errors });
      }
      res.status(500).json({ message: "Failed to update proxy rule" });
    }
  });

  app.delete("/api/proxy-rules/:id", async (req, res) => {
    try {
      const id = parseInt(req.params.id);
      await proxyManager.stopProxy(id);
      const deleted = await storage.deleteProxyRule(id);
      if (!deleted) {
        return res.status(404).json({ message: "Proxy rule not found" });
      }
      res.status(204).send();
    } catch (error) {
      res.status(500).json({ message: "Failed to delete proxy rule" });
    }
  });

  // Proxy Control API
  app.post("/api/proxy-rules/:id/start", async (req, res) => {
    try {
      const id = parseInt(req.params.id);
      const success = await proxyManager.startProxy(id);
      if (!success) {
        return res.status(400).json({ message: "Failed to start proxy" });
      }
      res.json({ message: "Proxy started successfully" });
    } catch (error) {
      res.status(500).json({ message: "Failed to start proxy" });
    }
  });

  app.post("/api/proxy-rules/:id/stop", async (req, res) => {
    try {
      const id = parseInt(req.params.id);
      const success = await proxyManager.stopProxy(id);
      if (!success) {
        return res.status(400).json({ message: "Failed to stop proxy" });
      }
      res.json({ message: "Proxy stopped successfully" });
    } catch (error) {
      res.status(500).json({ message: "Failed to stop proxy" });
    }
  });

  // Backend Servers API
  app.get("/api/backend-servers", async (req, res) => {
    try {
      const proxyRuleId = req.query.proxyRuleId ? parseInt(req.query.proxyRuleId as string) : undefined;
      const servers = await storage.getBackendServers(proxyRuleId);
      res.json(servers);
    } catch (error) {
      res.status(500).json({ message: "Failed to fetch backend servers" });
    }
  });

  app.post("/api/backend-servers", async (req, res) => {
    try {
      const validatedData = insertBackendServerSchema.parse(req.body);
      const server = await storage.createBackendServer(validatedData);
      res.status(201).json(server);
    } catch (error) {
      if (error instanceof z.ZodError) {
        return res.status(400).json({ message: "Invalid input", errors: error.errors });
      }
      res.status(500).json({ message: "Failed to create backend server" });
    }
  });

  app.put("/api/backend-servers/:id", async (req, res) => {
    try {
      const id = parseInt(req.params.id);
      const validatedData = insertBackendServerSchema.partial().parse(req.body);
      const server = await storage.updateBackendServer(id, validatedData);
      if (!server) {
        return res.status(404).json({ message: "Backend server not found" });
      }
      res.json(server);
    } catch (error) {
      if (error instanceof z.ZodError) {
        return res.status(400).json({ message: "Invalid input", errors: error.errors });
      }
      res.status(500).json({ message: "Failed to update backend server" });
    }
  });

  app.delete("/api/backend-servers/:id", async (req, res) => {
    try {
      const id = parseInt(req.params.id);
      const deleted = await storage.deleteBackendServer(id);
      if (!deleted) {
        return res.status(404).json({ message: "Backend server not found" });
      }
      res.status(204).send();
    } catch (error) {
      res.status(500).json({ message: "Failed to delete backend server" });
    }
  });

  // System Metrics API
  app.get("/api/system-metrics", async (req, res) => {
    try {
      const latest = await storage.getLatestSystemMetrics();
      res.json(latest || {
        cpuUsage: 0,
        memoryUsage: 0,
        networkIO: "0 MB/s",
        activeConnections: proxyManager.getActiveProxiesCount(),
        totalTraffic: "0GB",
        errorRate: "0%",
        avgResponseTime: 0
      });
    } catch (error) {
      res.status(500).json({ message: "Failed to fetch system metrics" });
    }
  });

  app.get("/api/system-metrics/history", async (req, res) => {
    try {
      const limit = req.query.limit ? parseInt(req.query.limit as string) : 100;
      const history = await storage.getSystemMetricsHistory(limit);
      res.json(history);
    } catch (error) {
      res.status(500).json({ message: "Failed to fetch metrics history" });
    }
  });

  // Proxy Logs API
  app.get("/api/proxy-logs", async (req, res) => {
    try {
      const limit = req.query.limit ? parseInt(req.query.limit as string) : 100;
      const proxyRuleId = req.query.proxyRuleId ? parseInt(req.query.proxyRuleId as string) : undefined;
      const logs = await storage.getProxyLogs(limit, proxyRuleId);
      res.json(logs);
    } catch (error) {
      res.status(500).json({ message: "Failed to fetch proxy logs" });
    }
  });

  app.delete("/api/proxy-logs", async (req, res) => {
    try {
      const olderThan = req.query.olderThan ? new Date(req.query.olderThan as string) : undefined;
      const deleted = await storage.clearProxyLogs(olderThan);
      res.json({ message: `Deleted ${deleted} log entries` });
    } catch (error) {
      res.status(500).json({ message: "Failed to clear proxy logs" });
    }
  });

  // System Operations API
  app.post("/api/system/restart", async (req, res) => {
    try {
      await proxyManager.restartAllProxies();
      res.json({ message: "All proxies restarted successfully" });
    } catch (error) {
      res.status(500).json({ message: "Failed to restart proxies" });
    }
  });

  app.get("/api/system/export-config", async (req, res) => {
    try {
      const rules = await storage.getProxyRules();
      const servers = await storage.getBackendServers();
      
      const config = {
        proxyRules: rules,
        backendServers: servers,
        exportedAt: new Date().toISOString()
      };
      
      res.setHeader('Content-Type', 'application/json');
      res.setHeader('Content-Disposition', 'attachment; filename=frp-config.json');
      res.json(config);
    } catch (error) {
      res.status(500).json({ message: "Failed to export configuration" });
    }
  });

  // Dynamic proxy middleware
  app.use((req, res, next) => {
    // Skip API routes and static files
    if (req.path.startsWith('/api') || req.path.startsWith('/src') || req.path.startsWith('/assets')) {
      return next();
    }

    // Try to find a matching proxy rule based on the port or host
    const host = req.get('host');
    const [hostname, port] = (host || '').split(':');
    
    if (port) {
      const portNum = parseInt(port);
      storage.getProxyRules().then(rules => {
        const matchingRule = rules.find(rule => 
          rule.remotePort === portNum && rule.status === 'running'
        );
        
        if (matchingRule) {
          const middleware = proxyManager.getProxyMiddleware(matchingRule.id);
          if (middleware) {
            return middleware(req, res, next);
          }
        }
        next();
      }).catch(() => next());
    } else {
      next();
    }
  });

  return httpServer;
}
